#include "SectorManager.h"

#include <stdio.h>

#include "base.h"
#include "Message.h"
#include "raknet\RakPeer.h"

using namespace RakNet;

SectorManager::SectorManager(): roomId(0), roomCount(0) {
	serverNode = RakPeerInterface::GetInstance();
	serverNode->SetIncomingPassword("airpassword", (int)strlen("airpassword"));
	serverNode->SetTimeoutTime(INFINITE, UNASSIGNED_SYSTEM_ADDRESS);
	SocketDescriptor socketDescriptor;
	socketDescriptor.port = 1001;
	if (serverNode->Startup(1000, &socketDescriptor, 1) == RAKNET_STARTED) {
		serverNode->SetMaximumIncomingConnections(1000);
		serverNode->SetOccasionalPing(true);
		serverNode->SetUnreliableTimeout(1000);
	} else {
		serverNode->Shutdown(300);
		RakPeerInterface::DestroyInstance(serverNode);
	}
}

SectorManager::~SectorManager() {
	serverNode->Shutdown(300);
	RakNet::RakPeerInterface::DestroyInstance(serverNode);
}

void SectorManager::processMessage() {
	Time timeStamp = 0;
	ushort typeId, flag, ret;

	Message msg;
	Message2 msgNew;

	if (p = serverNode->Receive()) {
		BitStream ibstream(p->data, p->length, false);
		ibstream.Read(flag);
		if (flag == (uchar)ID_TIMESTAMP) {
			ibstream.Read(timeStamp);
			ibstream.Read(typeId);
		}
		switch (typeId) {
		case ID_ROOM_CREATE:
			ibstream.Read(msg.length);
			ibstream.Read(msg.data, msg.length);
			createSector(&msg, serverNode->GetGuidFromSystemAddress(p->systemAddress));

			msgNew.typeId = ID_ROOM_CREATE_SUCCESS;
			msgNew.length = 0;
			msgNew.p = NULL;
			sendMessage(&msgNew, p->systemAddress);
			break;

		case ID_ROOM_JOIN:
			ibstream.Read(msg.length);
			ibstream.Read(msg.data, msg.length);
			ret = readySector(&msg, serverNode->GetGuidFromSystemAddress(p->systemAddress));

			msgNew.typeId = ret;
			msgNew.length = 0;
			msgNew.p = NULL;

			sendMessage(&msgNew, p->systemAddress);
			break;

		case ID_ROOM_LEAVE:
			ibstream.Read(msg.length);
			ibstream.Read(msg.data, msg.length);
			leaveSector(&msg, serverNode->GetGuidFromSystemAddress(p->systemAddress));
			break;

		case ID_ROOM_READY:
			ibstream.Read(msg.length);
			ibstream.Read(msg.data, msg.length);
			ret = readySector(&msg, serverNode->GetGuidFromSystemAddress(p->systemAddress));

			msgNew.typeId = ret;
			msgNew.length = 0;
			msgNew.p = NULL;

			sendMessage(&msgNew, p->systemAddress);
			break;

		case ID_ROOM_CANCEL_READY:
			ibstream.Read(msg.length);
			ibstream.Read(msg.data, msg.length);
			ret = readySector(&msg, serverNode->GetGuidFromSystemAddress(p->systemAddress));
			msgNew.typeId = ret;
			msgNew.length = 0;
			msgNew.p = NULL;
			sendMessage(&msgNew, p->systemAddress);
			break;

		case ID_ROOM_LOOKUP:
			packageRoomInfo(msgNew);
			sendMessage(&msgNew, p->systemAddress);
			break;
		case ID_GAME_START:
			break;

		case ID_GAME_START_CONFIRM:
			break;
		}
		serverNode->DeallocatePacket(p);
	}
}

void SectorManager::sendMessage(Message2* msg, const SystemAddress& addr) {
	bitStream.Reset();
	bitStream.Write((ushort)msg->typeId);

	switch(msg->typeId) {
	case ID_ROOM_CREATE_SUCCESS: case ID_ROOM_JOIN_SUCCESS: case ID_ROOM_READY_SUCCESS:
	case ID_ROOM_CANCEL_READY_SUCCESS: case ID_ROOM_NOT_EXIST: case ID_ROOM_FULL: case ID_PLAYER_NOT_EXIST:
		break;
	case ID_ROOM_LS:
		break;
	case ID_GAME_START_CONFIRM:
		break;
	}
	if (!msg->typeId == ID_ROOM_CREATE_SUCCESS) {
		bitStream.Write((ushort)msg->length);
		bitStream.Write((schar*)msg->p, (sint)msg->length);
	}
	serverNode->Send(&bitStream, 
		HIGH_PRIORITY, 
		RELIABLE_ORDERED, 
		0, 
		addr, 
		false
	);
}

void SectorManager::createSector(Message* msg, const RakNetGUID& guid) {
	SectorCreation* newSector = (SectorCreation*)msg->data;
	sectorContainer.insert(pair<int, PlayerSector>(roomId, 
		PlayerSector(string(newSector->sectorName), string(newSector->ownerId), guid))
	);
	++roomCount;
	++roomId; 
}

void SectorManager::leaveSector(Message* msg, const RakNetGUID& guid) {
	short roomId = (short)msg->data;
	map<int, PlayerSector>::iterator it = sectorContainer.find(roomId);
	if (it != sectorContainer.end()) {
		it->second.removePlayer(guid);
		if (it->second.playerCount == 0) {
			removeSector(it);
		} else {
			//forwardMessage(msg, it->second,guid);
		}
	} /* else {
		log();
	}*/
}

/**
 * @brief describe the behavior when a player choose a sector to join in
 *
 * @param msg 
 * @param guid
 *
 * @return the possible result for this operation:
 * ID_ROOM_FULL, ID_ROOM_JOIN_SUCCESS, ID_ROOM_READY
 */
int SectorManager::joinSector(Message* msg, const RakNetGUID& guid) {
	SectorJoin* sector = (SectorJoin*)msg->data;

	map<int, PlayerSector>::iterator it = sectorContainer.find(sector->roomId);
	if (it == sectorContainer.end()) return ID_ROOM_NOT_EXIST;

	if (it->second.playerCount < 10) {
		it->second.addPlayer(sector->playerId, guid);
		Message2 msgNew;
		msgNew.typeId = ID_ROOM_READY;
		msgNew.length = 0;
		msgNew.p = NULL;
		forwardMessage(&msgNew, it->second, guid);
		return ID_ROOM_JOIN_SUCCESS;
	} else {
		return ID_ROOM_FULL;
	}
}

/**
 * @brief 
 *
 * @param msg
 * @param guid
 *
 * @return 
 */
int SectorManager::readySector(Message* msg, const RakNetGUID& guid) {
	int roomId = (short)msg->data;
	map<int, PlayerSector>::iterator it = sectorContainer.find(roomId);
	if (it == sectorContainer.end()) {
		return ID_ROOM_NOT_EXIST;
	}
	if (it->second.readyPlayer(guid)) {
		Message2 msgNew;
		msgNew.typeId = ID_ROOM_READY;
		msgNew.length = 0;
		msgNew.p = NULL;
		forwardMessage(&msgNew, it->second, guid);
		return ID_ROOM_READY_SUCCESS;
	} else {
		return ID_PLAYER_NOT_EXIST;
	}
}

/**
 * @brief 
 *
 * @param msg
 * @param guid
 *
 * @return 
 */
int SectorManager::cancelReadySector(Message* msg, const RakNetGUID& guid) {
	int roomId = (short)msg->data;
	map<int, PlayerSector>::iterator it = sectorContainer.find(roomId);
	if (it == sectorContainer.end()) {
		return ID_ROOM_NOT_EXIST;
	}
	if (it->second.readyPlayer(guid)) {
		Message2 msgNew;
		msgNew.typeId = ID_ROOM_CANCEL_READY;
		msgNew.length = 1;
		msgNew.p = NULL;
		forwardMessage(&msgNew, it->second, guid);
		return ID_ROOM_CANCEL_READY_SUCCESS;
	} else {
		return ID_PLAYER_NOT_EXIST;
	}
}

void SectorManager::removeSector(const map<int, PlayerSector>::iterator& it) {
	sectorContainer.erase(it);
	--roomCount;
	//TODO recycle roomId
}

void SectorManager::packageRoomInfo(Message2& msg) {
	msg.typeId = ID_ROOM_LS;
}

void SectorManager::forwardMessage(Message2* msg, PlayerSector& playerSector, const RakNetGUID& guid) {
	map<RakNetGUID, Player>playerList = playerSector.getPlayerList();
	bitStream.Reset();
	bitStream.Write(msg->typeId);
	bitStream.Write((schar*)msg->p, msg->length);
	for (map<RakNetGUID, Player>::iterator it = playerList.begin(); it != playerList.end(); ++it) {
		if (it->first != guid) {
			serverNode->Send(&bitStream, 
				HIGH_PRIORITY, 
				RELIABLE_ORDERED, 
				0, 
				serverNode->GetSystemAddressFromGuid(it->first),
				false
			);
		}
	}
}
